<!--
 * @Description: 编辑弹窗
 * @Author: HuiSir<https://gitee.com/huisir001>
 * @Date: 2021-12-15 10:37:22
 * @LastEditTime: 2025-09-28 18:07:20
-->
<template>
  <el-dialog
    class="fy-form-dialog"
    append-to-body
    :close-on-press-escape="!fullscreen"
    v-model="showDialog"
    :fullscreen="fullscreen"
    :title="title"
    :width="width"
    :style="`--footer-align:${formCenter ? 'center' : 'right'}`"
    draggable
    @closed="closed"
  >
    <div v-loading="loading">
      <!-- 在form组件上使用v-if以确保在关闭弹窗时销毁form组件，以免列表多次打开弹窗发生数据冲突 -->
      <!-- 尤其是在有富文本编辑器时候会出错 -->
      <fy-form
        v-if="showDialog"
        ref="myForm"
        v-model="formParams"
        :options="formOptions"
        :formWidth="formWidth"
        :center="formCenter"
        :labelWidth="labelWidth"
        :inputWidth="inputWidth"
        :showBtns="false"
        :inline="false"
        @update:modelValue="emit('dataChange', $event)"
        @upload:change="bindUploadChange"
      >
        <template
          v-for="item in formOptions.filter((o) => o.slot)"
          :key="item.key"
          v-slot:[item.slot]
        >
          <slot :name="item.slot" :val="formParams" :options="formOptions" />
        </template>
      </fy-form>
    </div>
    <template #footer>
      <el-button plain @click="showDialog = false">取消</el-button>
      <el-button plain type="primary" @click="sure" :loading="submitLoading"
        >确认</el-button
      >
    </template>
  </el-dialog>
</template>

<script lang="ts" setup>
import { PropType, ref, watch } from "vue";
import {
  IFormOption,
  SubmitCallbacks,
  UploadEventParams,
  UploadChangeForm,
} from "../types";
import { deepCopy } from "../../helpers";

// 组件名称（必要）
defineOptions({ name: "FyEditDialog" });

// model方法改为defineModel语法糖
const showDialog = defineModel<boolean>({ default: false });

const props = defineProps({
  /**
   * 弹窗标题
   */
  title: {
    type: String,
    default: "编辑",
  },
  /**
   * 默认距顶部距离
   */
  top: {
    type: [String, Number],
    default: "15%",
  },
  /**
   * 宽度
   */
  width: {
    type: [String, Number],
    default: "500",
  },
  /**
   * 表单标签宽度
   */
  labelWidth: {
    type: [String, Number],
    default: 80,
  },
  /**
   * 表单输入框宽度
   */
  inputWidth: {
    type: [String, Number],
    default: "100%",
  },
  /**
   * 表单整体宽度
   */
  formWidth: {
    type: [String, Number],
    default: "100%",
  },
  /**
   * 表单水平居中
   */
  formCenter: {
    type: Boolean,
    default: false,
  },
  /**
   * 表单数据
   */
  params: {
    type: Object as PropType<Record<string, unknown>>,
    required: true,
    default: () => ({}),
  },
  /**
   * loading
   */
  loading: {
    type: Boolean,
    default: false,
  },
  /**
   * 是否全屏
   */
  fullscreen: {
    type: Boolean,
    default: false,
  },
});

/**
 * 表单项配置选项,允许动态修改
 */
const formOptions = defineModel("options", {
  type: Array as PropType<IFormOption[]>,
  required: true,
  default: () => [],
});

// emit
const emit = defineEmits<{
  // 元组语法：明确参数名称和类型
  submit: [
    formData: Record<string, unknown>,
    callbacks: SubmitCallbacks // 对象参数
  ];
  dataChange: [formData: Record<string, unknown>];
  "upload:change": [uploadParams: UploadEventParams, form: UploadChangeForm];
}>();

// 表单对象
const myForm = ref();
// 中转表单数据
const formParams = ref<Record<string, unknown>>({});
// 提交按钮loading
const submitLoading = ref(false);

// 表单验证
const validate = async () => {
  const valid = await myForm.value.validate().catch(() => {
    submitLoading.value = false;
  });
  if (!valid) {
    submitLoading.value = false;
  }
  return valid;
};

// 确认
const sure = () => {
  submitLoading.value = true;

  emit("submit", deepCopy(formParams.value), {
    validate,
    closeLoading() {
      submitLoading.value = false;
    },
    closeDialog() {
      showDialog.value = false;
    },
    uploadSubmit: myForm.value.uploadSubmit,
  });

  // 这里给出延时关闭loading,避免忘记执行closeLoading方法导致loading永久
  // 一般请求执行1.5秒也该关了
  const timer = setTimeout(() => {
    submitLoading.value = false;
    clearTimeout(timer);
  }, 1500);
};

// 弹窗关闭完成
const closed = () => {
  // 清除验证提示
  if (myForm.value) myForm.value.elForm.resetFields();
  // 清空表单信息
  formParams.value = {};
};

// 由于params数据(或其中部分数据)可能是异步的，所以这里监听赋值
watch(
  () => props.params,
  (params) => {
    // 初始化副本值
    Object.assign(formParams.value, params);
  },
  { immediate: true, deep: true }
);

/**
 * 文件更新事件
 */
const bindUploadChange = (uploadParams: UploadEventParams) => {
  emit("upload:change", uploadParams, {
    data: formParams,
    options: formOptions,
  });
};
</script>
